#include <e.h>
#include "e_mod_main.h"



static E_Config_DD *_core_config_entry=NULL;
static E_Config_DD *_widget_config_entry=NULL;

#define BUFFER_SIZE 1024
static char *bufferstring;


#define KIBIBYTE_FACTOR 1024.0
#define MEBIBYTE_FACTOR (1024.0 * 1024.0)
#define GIBIBYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
static char *_set_bytes(u_long val);


static Core *core;



/*
 ****************************************************************************************************** 
 *		Widget specs 
 * 
 ****************************************************************************************************** 
 */
static char *_set_bytes(u_long val)
{
	char *buf;
	buf=(char *)malloc(32*sizeof(char));
	memset(buf,0,32*sizeof(char));
	if ((val / KIBIBYTE_FACTOR)<1)
	{
		snprintf(buf,32*sizeof(char),"%.0f B",val);	

	}
	else 
		if ((val / MEBIBYTE_FACTOR)<1)
		{
			snprintf(buf,32*sizeof(char),"%.1f KiB",val / KIBIBYTE_FACTOR);	
		}
		else
		if ((val / GIBIBYTE_FACTOR)<1)
		{
			snprintf(buf,32*sizeof(char),"%.1f MiB",val / MEBIBYTE_FACTOR );
		}
		else
		{
			snprintf(buf,32*sizeof(char),"%.1f GiB",val / GIBIBYTE_FACTOR);
		}
	return buf;	
} 
 
static Widget *_widget_new(E_Module *m,E_Gadcon * gc, const char *name, const char *id, const char *style)
{
	E_Gadcon_Client *gcc;
	Widget *w;
	Core *c;
	if (!m)
		return;
	if (!m->data)
		return;
	c=m->data;
	Evas_Object *o, *of, *ob;
	int i=0;
	w = E_NEW (Widget, 1);
	w->m=m;
	w->conf=_core_widget_config_get(c,id);
	if (!_widget_theme_init(w,gc->evas))
	{
		e_module_dialog_show(w->m,D_("e17.net.module Desktop Widget"), D_("Theme init failed!"));
		_widget_free(w);
		return NULL;
	};
        
	gcc = e_gadcon_client_new (gc, name, id, style, w->canvas);
	gcc->data = w;
	w->gcc = gcc;
	evas_object_event_callback_add (w->canvas, EVAS_CALLBACK_MOUSE_DOWN,
				  _widget_cb_mouse_down, w);	
	return w;
}

static void _widget_free(Widget *w)
{
	if (w)
	{
		if (w->config_dialog != NULL)
    		e_object_del (E_OBJECT (w->config_dialog));		
  		evas_object_event_callback_del (w->canvas, EVAS_CALLBACK_MOUSE_DOWN,_widget_cb_mouse_down);
		_widget_theme_shutdown(w);
		E_FREE(w);
	}	
}

static int	_widget_theme_init(Widget *w, Evas *evas)
{
	char buf[PATH_MAX];
	if (!w->canvas)
	{
		if (evas)
		{
			w->canvas = edje_object_add (evas);
		}
		else
		{
			return 0;
		}
	}
	else
	{
		evas_object_hide(w->canvas);
	}
	snprintf (buf, sizeof (buf), "%s/themes/%s",e_module_dir_get (w->m),w->conf->theme);
	if (!edje_object_file_set (w->canvas, buf, "modules/e17.net.module/main"))
    {
    	snprintf (buf, sizeof (buf), "%s/themes/%s",e_module_dir_get (w->m),DEFAULT_THEME);
    	if (!edje_object_file_set (w->canvas, buf, "modules/e17.net.module/main"))
    		return 0;
    	e_module_dialog_show(w->m,D_("e17.net.module Desktop Widget"), D_ ("e17.net.module Theme init <hilight> failed!</hilight><br> Loaded e17.net.module default Theme."));	
    }
/*
 * UnPremul ARG 		(Color << 8)/Alpha
 * Premul ARG			(Color*Alpha) >> 8
 * Each time color should be passed in premul 
 * if we want to detect rgb we should call evas_object_color_get and manualy unpremul them
 */  	
  	int tmp;
  	tmp=((255*w->conf->alpha) >> 8);
  	evas_object_color_set(w->canvas,tmp,tmp,tmp, w->conf->alpha);
  	evas_object_show(w->canvas);
  	return 1;	
}

static void _widget_theme_shutdown(Widget *w)
{
	if (w->canvas)
	{
		evas_object_del(w->canvas);
		w->canvas=NULL;	
	}
}

static void _widget_cb_mouse_down (void *data, Evas * e, Evas_Object * obj, void *event_info)
{
  Widget *w;
  Evas_Event_Mouse_Down *ev;
  w = data;
  ev = event_info;
  if ((ev->button == 3) && (!w->menu))
    {
      E_Menu *mn;
      E_Menu_Item *mi;
      int x, y, width, height;

      mn = e_menu_new ();
      e_menu_post_deactivate_callback_set (mn, _widget_menu_cb_post, w);
      w->menu = mn;

      mi = e_menu_item_new (mn);
      e_menu_item_label_set (mi, D_("Configuration"));
      e_util_menu_item_edje_icon_set (mi, "enlightenment/configuration");
      e_menu_item_callback_set (mi, _widget_menu_cb_configure, w);

      mi = e_menu_item_new (mn);
      e_menu_item_separator_set (mi, 1);

      e_gadcon_client_util_menu_items_append (w->gcc, mn, 0);
      e_gadcon_canvas_zone_geometry_get (w->gcc->gadcon, &x, &y, &width, &height);
      e_menu_activate_mouse (mn,
			     e_util_zone_current_get (e_manager_current_get
						      ()), x + ev->output.x,
			     y + ev->output.y, 1, 1,
			     E_MENU_POP_DIRECTION_DOWN, ev->timestamp);
      evas_event_feed_mouse_up (w->gcc->gadcon->evas, ev->button,
				EVAS_BUTTON_NONE, ev->timestamp, NULL);
    }
}

static void _widget_menu_cb_configure (void *data, E_Menu * m, E_Menu_Item * mi)
{
	Widget *w;
	w = data;
	if (!w)
		return;
	if (w->config_dialog!=NULL)  
		return;
	printf("create config dialog\n");	
	w->config_dialog=_net_widget_config(w);
}

static void _widget_menu_cb_post (void *data, E_Menu * m)
{
	Widget *w;		
	w = data;
	if (w)
	{
  		if (w->menu == NULL)
    		return;
		e_object_del(E_OBJECT(w->menu));
		w->menu = NULL;
	}	
}

static void _widget_edje_message_send(int id, int message, void *data)
{
	
}


void _net_widget_config_updated(Widget *w)
{
	if (!w)
		return;	
	_widget_theme_init(w,NULL);
}






/*
 ****************************************************************************************************** 
 * Core Specs
 * 
 ****************************************************************************************************** 
 */


static Core *_core_new(void)
{
	Core *c;
	bufferstring=calloc(1,BUFFER_SIZE*sizeof(char));
	c = E_NEW(Core, 1);
   	if (!c)
   	  return NULL;
   	if (!_core_config_load(c))
   	{
   		
   	};
   	c->timer=ecore_timer_add (c->conf->poll_time, _core_cb_update, c);
   	e_gadcon_provider_register(&_gc_class);
  	return c;
   	
}

static void _core_free(Core *c)
{
	if (!c)
		return;
	if (c->config_dialog != NULL)
    	e_object_del (E_OBJECT (c->config_dialog));
	if (c->timer!=NULL)
		ecore_timer_del (c->timer);
	if (c->conf->theme)
		evas_stringshare_del(c->conf->theme);	
	while (c->conf->items)
    {
		Widget_Config *wc;
		wc = c->conf->items->data;
		c->conf->items = evas_list_remove_list (c->conf->items, c->conf->items);
		if (wc->id != NULL)
			evas_stringshare_del (wc->id);
		if (wc->theme!=NULL)
			evas_stringshare_del (wc->theme);
      	E_FREE (wc);
    }
	e_gadcon_provider_unregister (&_gc_class);
	E_FREE(c->conf);
	E_FREE(bufferstring);
	E_FREE(c);
	E_CONFIG_DD_FREE (_widget_config_entry);
	E_CONFIG_DD_FREE (_core_config_entry);
}

static int _core_config_save(Core *c)
{
	if (!c)
		return;
  	Evas_List *l;
	Widget *w;
    Widget_Config *wc;
	for (l = c->widgets; l; l = l->next)
    {
		w = l->data;
		wc = _core_widget_config_get(c,w->gcc->id);
		if (wc->id)
			evas_stringshare_del (wc->id);
		wc->id = evas_stringshare_add(w->gcc->id);
	}
	e_config_domain_save ("e17.net.module", _core_config_entry, c->conf);
	return 1;
}

static int _core_config_load(Core *c)
{
	if (!c)
		return;
		
	_widget_config_entry = E_CONFIG_DD_NEW ("e17.net.module.widget", Widget_Config);
#undef T
#undef D
#define T Widget_Config
#define D _widget_config_entry
	E_CONFIG_VAL (D, T, id, STR);
	E_CONFIG_VAL (D, T, theme, STR);
	E_CONFIG_VAL (D, T, alpha, INT);
	E_CONFIG_VAL (D, T, have_popup, INT);
	E_CONFIG_VAL (D, T, iface, STR);
#undef T
#undef D
	_core_config_entry = E_CONFIG_DD_NEW ("e17.net.module.core",Core_Config);
#define T Core_Config
#define D _core_config_entry
	E_CONFIG_VAL (D, T, version, INT);
	E_CONFIG_VAL (D, T, notification, INT);
	E_CONFIG_VAL (D, T, poll_time, INT);
	E_CONFIG_VAL (D, T, theme, STR);
	E_CONFIG_VAL (D, T, alpha, INT);
	E_CONFIG_LIST (D, T, items, _widget_config_entry);
	c->conf = e_config_domain_load ("e17.net.module", _core_config_entry);
	if (c->conf == NULL)
    {
		c->conf = E_NEW (Core_Config, 1);
		c->conf->version = CONFIG_VERSION;
		c->conf->notification=1;
		c->conf->poll_time=1.0;
		c->conf->theme=evas_stringshare_add(DEFAULT_THEME);
		c->conf->alpha=255;
		
    }
}

/**
 * Function that is called on timer every second
 * 
 * 
 */

#ifdef __FreeBSD__
static int _core_cb_update(void *data)
{
/**
 * Get all iface stats using getifaddrs
 * then for each widget check 
 * 		- if widget->iface  n/a get first available iface
 * 			-and update its basic vars
 * 		- if we can`t retrieve for specified iface name stats just show N/A in text filed and 
 */
 	Core *c;
 	Widget *w;
	Evas_List *l;
	struct ifaddrs *ifap, *ifa;
	struct if_data *ifd;
 	char *tmp1,*tmp2,*tmp3;
 	struct iface_stats stats;
 	c=data;
 	if (!c)
 		return;

	if (c->widgets)
	{
		if (getifaddrs(&ifap) < 0)
			return;
			
		for (l = core->widgets; l; l = l->next)
	    	{
	    		w = l->data;

	    		stats.link_state=0;
				stats.baudrate=0;
				stats.ipackets=0;
				stats.opackets=0;
				stats.ibytes=0;
				stats.obytes=0;
	    		for (ifa = ifap; ifa; ifa = ifa->ifa_next) 
				{
					if (ifa->ifa_data)
					{
						if (strcmp(ifa->ifa_name,w->conf->iface)==0)
						{
							ifd=ifa->ifa_data;
							stats.link_state=ifd->ifi_link_state;
							stats.baudrate=ifd->ifi_baudrate;
							stats.ipackets=ifd->ifi_ipackets;
							stats.opackets=ifd->ifi_opackets;
							stats.ibytes=ifd->ifi_ibytes;
							stats.obytes=ifd->ifi_obytes;
						}
					}
				}	
	    		int t1,t2;
	    		float p1,p2;
	    		t1=abs(stats.ibytes - w->ibytes)/core->conf->poll_time;
	    		t2=abs(stats.obytes-  w->obytes)/core->conf->poll_time;
	    		p1=(t1/stats.baudrate);
	    		p2=(t2/stats.baudrate);
//	    		printf("%d,%d\n",tmp1,tmp2);
	    		tmp1=_set_bytes(stats.baudrate);
				tmp2=_set_bytes(t1);
				tmp3=_set_bytes(t2);
				snprintf(bufferstring, BUFFER_SIZE,D_("%s<br>%s/s<br>%s/s"),w->conf->iface,tmp2,tmp3);
//				snprintf(bufferstring, BUFFER_SIZE,D_("%s<br>%.2f%%<br>%.2f%%"),w->conf->iface,t1/stats.baudrate,t2/stats.baudrate);
				w->link_state=stats.link_state;
				w->baudrate=stats.baudrate;
				w->ipackets=stats.ipackets;
				w->opackets=stats.opackets;
				w->ibytes=stats.ibytes;
				w->obytes=stats.obytes;
				E_FREE(tmp1);
				E_FREE(tmp2);
				E_FREE(tmp3);
	    	   	edje_object_part_text_set (w->canvas, "net_text",bufferstring );
	    	}
		freeifaddrs(ifap);
	}
	return 1;
}

#endif
#ifdef __linux__
static int _core_cb_update(void *data)
{
	Evas_List *ilist,*l,*next;
	If_Stats *stats;
	Core *c;
 	Widget *w;
	char *tmp1,*tmp2,*tmp3;
	FILE *stat;
  	char dev[64];
  	char buf[256];
  	unsigned long in = 0;
  	unsigned long out = 0;
  	unsigned long dummy = 0;
  	int found;
  	double i, o;

	c=data;
 	if (!c)
 		return;
 		
 	stat = fopen ("/proc/net/dev", "r");
	if (!stat)
    	return 1;
  	while (fgets (buf, 256, stat))
    {
     	int i = 0;
		
		for (; buf[i] != 0; i++)
			if (buf[i] == ':')
	  			buf[i] = ' ';
		stats=E_NEW(If_Stats,1);
		if (!stats)
			return;
      	if (sscanf (buf, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
		  "%lu %lu %lu %lu\n", stats->if_name, &stats->ibytes, &dummy, &dummy,
		  &dummy, &dummy, &dummy, &dummy, &dummy, &stats->obytes, &dummy,
		  &dummy, &dummy, &dummy, &dummy, &dummy, &dummy) < 17)
		continue;
		ilist=evas_list_append(ilist,stats);
    }
  fclose (stat);
  
  
  if (c->widgets)
	{		
		for (l = core->widgets; l; l = l->next)
	    	{
	    		w = l->data;
				for (next = ilist; next; next = next->next)
   				{
   					if (next->data)
   					{
						stats=next->data;
						if (strcmp(stats->if_name,w->conf->iface)==0)
						{
							found=1;
							break;
						}
   					}
					
				}	
	    		int t1,t2;
	    		float p1,p2;
	    		t1=abs(stats->ibytes - w->ibytes)/core->conf->poll_time;
	    		t2=abs(stats->obytes-  w->obytes)/core->conf->poll_time;
	    		
//	    		printf("%d,%d\n",tmp1,tmp2);
				tmp2=_set_bytes(t1);
				tmp3=_set_bytes(t2);
				snprintf(bufferstring, BUFFER_SIZE,D_("%s<br>%s/s<br>%s/s"),w->conf->iface,tmp2,tmp3);
//				snprintf(bufferstring, BUFFER_SIZE,D_("%s<br>%.2f%%<br>%.2f%%"),w->conf->iface,t1/stats.baudrate,t2/stats.baudrate);
				if (!found)
					w->link_state=0;
				//w->baudrate=stats.baudrate;
				w->ipackets=stats->ipackets;
				w->opackets=stats->opackets;
				w->ibytes=stats->ibytes;
				w->obytes=stats->obytes;
				E_FREE(tmp1);
				E_FREE(tmp2);
				E_FREE(tmp3);
	    	   	edje_object_part_text_set (w->canvas, "net_text",bufferstring );
	    	}
	}   	
	    	
  		for (next = ilist; next; next = next->next)
   				{
   					if (next->data)
   					{
						stats=next->data;
						E_FREE(stats);
   					}
					
				}
	return 1;			
}
#endif


static Widget *_core_widget_add(Core *c,E_Gadcon * gc, const char *name, const char *id, const char *style)
{
	Widget *w;
	if (!c)
		return NULL;
	w=_widget_new(c->m,gc,name,id,style);
	if (!w)
		return NULL;
	c->widgets = evas_list_append(c->widgets, w);
	_core_cb_update(w);
  	return w;		
}

static Widget_Config *_core_widget_config_get(Core *c, const char *id)
{
	Evas_List *l;
	Widget_Config *wc;
	if (!c)
		return NULL;
	for (l = c->conf->items; l; l = l->next)
	{
		wc = l->data;
		if (wc->id == NULL)
			continue;
		if (strcmp (wc->id, id) == 0)
			return wc;
    }
    wc = E_NEW (Widget_Config, 1);
    wc->id = evas_stringshare_add (id);
    wc->theme =evas_stringshare_add(c->conf->theme);
    wc->alpha = c->conf->alpha;
    wc->have_popup = 0;
//Should be removed with function call that will output first available iface
	struct ifaddrs *ifap, *ifa;
	struct if_data *ifd;
	if (getifaddrs(&ifap) < 0)
		return;
	for (ifa = ifap; ifa; ifa = ifa->ifa_next) 
	{
		//	ifd=ifa->ifa_data;
		if (ifa->ifa_data)
		{
			wc->iface=evas_stringshare_add(ifa->ifa_name);
			break;	
		}
	}
	freeifaddrs(ifap);
	if (!wc->iface)
		wc->iface=evas_stringshare_add("n/a");
    c->conf->items = evas_list_append (c->conf->items, wc);
  return wc;	
}
static void _core_widget_delete(Core *c, Widget *w)
{
	if (!c)
		return;
	if (!w)
		return;
	c->widgets = evas_list_remove(c->widgets, w);
	_widget_free(w);
}

void _net_core_config_updated(Core *c)
{
	if (!c)
		return;
	ecore_timer_del (c->timer);	
	c->timer=ecore_timer_add (c->conf->poll_time, _core_cb_update, c);
}



/*
 ****************************************************************************************************** 
 *		Gadcon specs 
 * 
 ****************************************************************************************************** 
 */
 
 static E_Gadcon_Client *_gc_init (E_Gadcon * gc, const char *name,const char *id, const char *style)
{
	Widget *w;
	w = _core_widget_add(core,gc,name,id,style);
	if (!w)
		return NULL;
	else
		return w->gcc;
}


static void _gc_shutdown (E_Gadcon_Client * gcc)
{
	E_Module *m;
	Core *c;
	Widget *w;
	if (!gcc)
		return;
	w = gcc->data;
	if (!w)
		return;
	m=w->m;
	if (!m)	
		return;
	c=m->data;
	if (!c)
		return;
	_core_widget_delete(c,w);
  
}
static void _gc_orient (E_Gadcon_Client * gcc)
{
	Widget *w;
	Evas_Coord width, height;
	w = gcc->data;
	if (!w)
		return;
	switch (gcc->gadcon->orient)
	{
		case E_GADCON_ORIENT_FLOAT:
		case E_GADCON_ORIENT_HORIZ:
		case E_GADCON_ORIENT_VERT:
		case E_GADCON_ORIENT_LEFT:
		case E_GADCON_ORIENT_RIGHT:
		case E_GADCON_ORIENT_TOP:
		case E_GADCON_ORIENT_BOTTOM:
		case E_GADCON_ORIENT_CORNER_TL:
		case E_GADCON_ORIENT_CORNER_TR:
		case E_GADCON_ORIENT_CORNER_BL:
		case E_GADCON_ORIENT_CORNER_BR:
		case E_GADCON_ORIENT_CORNER_LT:
		case E_GADCON_ORIENT_CORNER_RT:
		case E_GADCON_ORIENT_CORNER_LB:
		case E_GADCON_ORIENT_CORNER_RB:
			width = 16;
			height = 16;
		default:
		break;
	}
	
	edje_object_size_min_calc (w->canvas, &width, &height);
	e_gadcon_client_min_size_set (gcc, width, height);
}
static char *_gc_label (void)
{
	return D_("e17.net.module");
}
static Evas_Object *_gc_icon (Evas * evas)
{
	Evas_Object *o;
	char buf[4096];
	o = edje_object_add (evas);
	snprintf (buf, sizeof (buf), "%s/data/module.edj",e_module_dir_get(core->m));
	edje_object_file_set (o, buf, "icon");
	return o;
}

/*
 ****************************************************************************************************** 
 * E_MODULE_API Staff 
 * dl directly look into module.so and fill references
 ****************************************************************************************************** 
 */
 
EAPI E_Module_Api e_modapi = 
   {
      E_MODULE_API_VERSION,
      "e17.net.module"
   };
   
EAPI void *e_modapi_init (E_Module *m)
{
//  Core *c;
  bindtextdomain(PACKAGE, LOCALEDIR);
  bind_textdomain_codeset (PACKAGE, "UTF-8");
  core = _core_new();
  printf("New Core created\n");
  core->m = m;
  m->data = core;
  return core;
}

EAPI int
e_modapi_config(E_Module *m)
{
   if (!m) return 0;
   if (core->config_dialog)
   		return 0;
   core->config_dialog=_net_core_config(core);
   return 1;
}


EAPI int e_modapi_shutdown (E_Module *m)
{
	Core *c;
	c = m->data;
	if (c)
	   _core_free(c);
  return 1;
}

EAPI int e_modapi_save (E_Module *m)
{
  Core *c;
  c = m->data;
  if(c)
  	{
  		_core_config_save(c);
  	}
  return 1;
}

EAPI int e_modapi_about (E_Module *m)
{
  e_module_dialog_show (m, D_ ("Enlightenment Net Monitor Module"),
			D_ ("This module is used to monitor System Network interfaces."));
  return 1;
}
